﻿using System.Collections.ObjectModel;
using System.Collections.Specialized;
using PluginSample2.BarModel;
using PluginSample2.Common;
using Plugins;
using StockModel;

namespace PluginSample2
{
	internal class DecissionEngine
	{
		#region Attributes

		private readonly EngineSettingsBase _settings;
		private readonly ObservableCollection<Quotation> _quotationSource;
		private readonly ObservableCollection<Bar> _barSource;

		private readonly OperationManager _operationManager;

		#endregion

		#region Properties

		public DecisionEngineData Data { get; private set; }

		#endregion

		#region Public Methods

		public DecissionEngine(ObservableCollection<Bar> barSource, ObservableCollection<Quotation> quotationSource, EngineSettingsBase settings, OperationManager operationManager, DecisionEngineData data)
		{
			_operationManager = operationManager;

			_barSource = barSource;
			_quotationSource = quotationSource;
			_settings = settings;

			_barSource.CollectionChanged += BarSource_CollectionChanged;
			_quotationSource.CollectionChanged += QuotationSource_CollectionChanged;

			this.Data = data;
		}

		#endregion

		#region Event Handlers

		private void QuotationSource_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
		{
			if (e.Action == NotifyCollectionChangedAction.Add)
			{
				this.Data.LastQuotation = (Quotation)e.NewItems[e.NewItems.Count - 1]; // Only take the last quotation received (we will receive always one as loaders do it one by one)
				
				if (this.Data.State == DecisionEngineState.OnLongPossition || this.Data.State == DecisionEngineState.OnShortPossition)
				{
					this.EvaluateStateMachine();	
				}
				this.Data.ProcessedDay = this.Data.LastQuotation.Date.Date;
			}
		}

		private void BarSource_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
		{
			if (e.Action == NotifyCollectionChangedAction.Add)
			{
				this.Data.PreviousBar = this.Data.CurrentBar;
				this.Data.CurrentBar = (Bar)e.NewItems[0];
				
				if (this.Data.PreviousBar != null)
				{
					this.EvaluateStateMachine();
				}
			}
		}

		#endregion

		#region Private Methods

		private void EvaluateStateMachine()
		{
			if (this.Data.State == DecisionEngineState.WaitingOpen)
			{
				if (this.Data.LastQuotation.Date.TimeOfDay > _settings.LastOperationStart)
				{
					this.Data.State = DecisionEngineState.DayClosed;
				}
				else if (this.Data.LastQuotation.Volume > 0)
				{
					this.Data.State = DecisionEngineState.Stall;
				}
			}

			if (this.Data.State == DecisionEngineState.Stall)
			{
				if (this.Data.LastQuotation.Date.TimeOfDay > _settings.LastOperationStart)
				{
					this.Data.State = DecisionEngineState.DayClosed;
				}
				else if ((_settings.RequirePureBars && (this.Data.CurrentBar.Minimum > this.Data.PreviousBar.Minimum && this.Data.CurrentBar.Maximum > this.Data.PreviousBar.Maximum) || !_settings.RequirePureBars && (this.Data.CurrentBar.Maximum > this.Data.PreviousBar.Maximum)) && _settings.ExecutionDirection != ExecutionDirection.Down)
				{
					this.Data.State = DecisionEngineState.CountingUp;
					// this.Data.BarCount++; Don't add here the barcount, will be added on the countingup state just after this one (on the same cycle)
				}
				else if (_settings.RequirePureBars && (this.Data.CurrentBar.Minimum < this.Data.PreviousBar.Minimum && this.Data.CurrentBar.Maximum < this.Data.PreviousBar.Maximum) || !_settings.RequirePureBars && (this.Data.CurrentBar.Minimum < this.Data.PreviousBar.Minimum) && _settings.ExecutionDirection != ExecutionDirection.Up)
				{
					this.Data.State = DecisionEngineState.CountingDown;
					// this.Data.BarCount++; Don't add here the barcount, will be added on the countingup state just after this one (on the same cycle)
				}
			}

			if (this.Data.State == DecisionEngineState.CountingUp)
			{
				if (this.Data.LastQuotation.Date.TimeOfDay > _settings.LastOperationStart)
				{
					this.Data.State = DecisionEngineState.DayClosed;
				}
				else if ((_settings.RequirePureBars && this.Data.CurrentBar.Minimum > this.Data.PreviousBar.Minimum && this.Data.CurrentBar.Maximum > this.Data.PreviousBar.Maximum) || (!_settings.RequirePureBars && this.Data.CurrentBar.Minimum > this.Data.PreviousBar.Minimum))
				{
					this.Data.BarCount++;
				}
				else if (this.Data.CurrentBar.Minimum < this.Data.PreviousBar.Minimum)
				{
					if (this.Data.CurrentBar.Maximum < this.Data.PreviousBar.Maximum)
					{
						this.Data.State = DecisionEngineState.CountingDown;
						this.Data.BarCount = 1;
					}
					else
					{
						this.Data.State = DecisionEngineState.Stall;
						this.Data.BarCount = 0;
					}
				}

				if (this.Data.BarCount >= _settings.BarQuantity && this.Data.State == DecisionEngineState.CountingUp)
				{
					this.Data.State = DecisionEngineState.OnLongPossition;
					_operationManager.OpenLongPossition(_settings.QuantityToBuy);
				}

			}

			if (this.Data.State == DecisionEngineState.CountingDown)
			{
				if (this.Data.LastQuotation.Date.TimeOfDay > _settings.LastOperationStart)
				{
					this.Data.State = DecisionEngineState.DayClosed;
				}
				else if ((_settings.RequirePureBars && this.Data.CurrentBar.Maximum < this.Data.PreviousBar.Maximum && this.Data.CurrentBar.Minimum > this.Data.PreviousBar.Minimum) || (!_settings.RequirePureBars && this.Data.CurrentBar.Maximum < this.Data.PreviousBar.Maximum))
				{
					this.Data.BarCount++;
				}
				else if (this.Data.CurrentBar.Maximum > this.Data.PreviousBar.Maximum)
				{
					if (this.Data.CurrentBar.Minimum < this.Data.PreviousBar.Minimum)
					{
						this.Data.State = DecisionEngineState.CountingUp;
						this.Data.BarCount = 1;
					}
					else
					{
						this.Data.State = DecisionEngineState.Stall;
						this.Data.BarCount = 0;
					}
				}

				if (this.Data.BarCount >= _settings.BarQuantity && this.Data.State == DecisionEngineState.CountingDown)
				{
					this.Data.State = DecisionEngineState.OnShortPossition;
					_operationManager.OpenShortPossition(_settings.QuantityToBuy);
				}
			}

			if (this.Data.State == DecisionEngineState.OnLongPossition)
			{
				if (this.Data.LastQuotation.Date.TimeOfDay >= _settings.ForcedCloseTime || this.Data.LastQuotation.Buyers[0].Value < this.Data.PreviousBar.Minimum)
				{
					_operationManager.CloseLongPossition();

					this.Data.BarCount = 0;
					this.Data.State = this.Data.LastQuotation.Date.TimeOfDay >= _settings.ForcedCloseTime ? DecisionEngineState.DayClosed : DecisionEngineState.Stall;
				}
			}

			if (this.Data.State == DecisionEngineState.OnShortPossition)
			{
				if (this.Data.LastQuotation.Date.TimeOfDay >= _settings.ForcedCloseTime || this.Data.LastQuotation.Sellers[0].Value > this.Data.PreviousBar.Maximum)
				{
					_operationManager.CloseShortPossition();	

					this.Data.BarCount = 0;
					this.Data.State = this.Data.LastQuotation.Date.TimeOfDay >= _settings.ForcedCloseTime ? DecisionEngineState.DayClosed : DecisionEngineState.Stall;
				}
			}

			if (this.Data.State == DecisionEngineState.DayClosed)
			{
				if (this.Data.LastQuotation.Date.TimeOfDay < _settings.LastOperationStart && this.Data.ProcessedDay != this.Data.LastQuotation.Date.Date)
				{
					this.Data.State = DecisionEngineState.WaitingOpen;
					this.Data.BarCount = 0;
					this.Data.CurrentBar = null;
					this.Data.PreviousBar = null;
				}
			}
		}

		#endregion
	}
}
